/************************************************************************
Copyright (c) 2010, 2013, C.R. All rights reserved. 

E-mail: crtoyou@gmail.com; crtoyou@163.com

You can redistribute it and/or modify it under the terms of the
GNU Lesser General Public License as published by the
Free Software Foundation version 3 of the License.

This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. if not, write to the Free Software Foundation, Inc., 
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
************************************************************************/

#ifndef __CR_BIT_STR_ASM_OPTIMIZED_H__
#define __CR_BIT_STR_ASM_OPTIMIZED_H__

#include <crdefine.h>
#include <crdebug.h>

//////////////////////////////////////////////////////////////////////////
// you can define any macro of bellows for optimizing if necessary
//
//#define BIT_SRC_FROM0
//#define BIT_DST_FROM0
//#define BITFILL_FROM0
//#define BITREAD_FROM0
//#define BITSEEK_FROM0
//
// notice: all the functions did not check buffers' size
// you should ensure buffers' safety before each call
//////////////////////////////////////////////////////////////////////////


/**************************** < REGISTORS LAYOUT > ***************************/
/*  EAX +---------------+---------------+-------------input-------------+    */
/*  EBX +---------------+---------------+-------------cache-------------+    */
/*  ECX +---------------+---------------+-----length----+----offset-----+    */
/*  EDX +--------------------------bits-remained------------------------+    */
/*  ESP +---------------+---------------+---------------+---------------+    */
/*  EBP +---------------------------------------------------------------+    */
/*  ESI +-------------------------source-address------------------------+    */
/*  EDI +-------------------------target-address------------------------+    */
/*****************************************************************************/

//****************************** bit alignment ******************************//
//                                                                           //
//     low bits first, small endian.                                         //
//                                                                           //
//         word                 |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|            //
//         bits                       |-+-+-+-+-+-+-|                        //
//                              |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|            //
//                    order-By: |1-1-1-1-1-1-1-1|0-0-0-0-0-0-0-0|            //
//                    order-bi: |7-6-5-4-3-2-1-0|7-6-5-4-3-2-1-0|            //
//                                                                           //
//                 ***  << step a byte <<  ***                               //
//                                                                           //
//         word |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|                            //
//         bits               |-+-+-+-|                                      //
//              |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|                            //
//    order-By: |2-2-2-2-2-2-2-2|1-1-1-1-1-1-1-1|                            //
//    order-bi: |7-6-5-4-3-2-1-0|7-6-5-4-3-2-1-0|                            //
//                                                                           //
//***************************************************************************//

// tips:
// when read a bit you can _asm {ROR [Byte], 1} and test if CF/SF is true

__forceinline void __fastcall 
bitcopy(void* dstbuf, const void* srcbuf, uint16 len, byte dstfrom = 0, byte srcfrom = 0)
{
	ASSERT(dstbuf);
	ASSERT(srcbuf);
	ASSERT(dstfrom < 8);
	ASSERT(srcfrom < 8);
	ASSERT(len > 0);

	_asm {

		pushad;

		// BX for input, AX for output

		mov esi, [srcbuf];
		mov edi, [dstbuf];
		mov dx, [len];

		movzx bx, [esi];
		inc esi;

#ifndef BIT_SRC_FROM0
		// ignore bits before ibSrcFrom
		mov cl, [srcfrom];
		shr bx, cl;
		mov ch, 8;
		sub ch, cl;
#else
		mov ch, 8;
#endif

#ifndef BIT_DST_FROM0
		// retain bits before ibDstFrom
		mov cl, [dstfrom];
		movzx ax, [edi];
		//don't inc EDI here, since it will be written back at where. 
		ror ax, cl;
		xor al, al;
		rol ax, cl;
#else
		xor ax, ax;
		xor cl, cl;
#endif

LOP:
		// CL is current target offset
		// CH is the bit length of each loop

		sub dl, ch;
		sbb dh, 0;
		jge PRC;

		add ch, dl;
		jle FNL;

PRC:
		//align the input to cache
		shl bx, cl;
		//cache the input space
		or ax, bx;
		add cl, ch;
		cmp cl, 8;
		jl NXT; 

		//PUT:
		//output low cache if necessary
		mov [edi], al;
		inc edi;
		movzx ax, ah; //instead of <MOV BL, BH> and <XOR BH, BH>
		sub cl, 8;

NXT:
		// load source byte
		movzx bx, [esi];
		inc esi;
		mov ch, 8;

		jmp LOP;

FNL:
		// flush cache, combined with target redundant
		mov ah, [edi]; //load [EDI] for redundant
		ror ax, cl;
		rol ah, cl;
		mov [edi], ah;

//FIN:
		popad;

	}

}


__forceinline void __fastcall 
bitfill(const bool* vals, void* bitsbuf, uint16 len, byte bitsfrom = 0)
{
	ASSERT(bitsbuf);
	ASSERT(bitsfrom < 8);
	ASSERT(vals);
	ASSERT(len > 0);

	_asm {

		pushad;

		// BX for input, AX for output

		mov esi, [vals]; //input the bool or char value for a bit
		mov edi, [bitsbuf]; //output the filled target bits
		mov dx, [len];

		//CH is target remained bits in one byte.
		//(it is deferent from bitcpy). 

#ifndef BITFILL_FROM0
		// ignore bits before ibSrcFrom
		mov al, [edi]; //load target head, as if it is from source.
		//don't INC EDI here, since it will write back at where. 
		mov cl, [bitsfrom];
		ror al, cl; //notice below:
		// notice: ///////////////////////////////////////////////////////////////
		//now the invalid bits are moved to high bits, as if pushed in from high bit by bit. 
		//after each PRC done you will get the bits aligned to BIT-0.
		//////////////////////////////////////////////////////////////////////////
		mov ch, 8;
		sub ch, cl;
		xor cl, cl;
#else
		mov cx, 0800h;
#endif

LOP:
		// CH is the bit length of each loop
		sub dl, ch;
		sbb dh, 0;
		jge PRC;

		//DL is less than 8 now, is negative length of redundant. 
		sub cl, dl;
		add ch, dl;
		jle FIN;

PRC:
		mov bl, [esi];
		inc esi;
		add bl, 0FFh; //make CF change if bl not 0
		rcr al, 1; //push the CF into cache from high bit, CF is filled above
		// push example: /////////////////////////////////////////////////////////
		// before pushed : C=1 AL=111.....
		// push the CF in: C=. AL=1111....
		//////////////////////////////////////////////////////////////////////////

		dec ch;
		jnz PRC;

		test cl, cl;
		jne FNL;

		mov [edi], al;
		inc edi;
		mov ch, 8;

		jmp LOP;

FNL:
		mov ah, [edi]; //load [EDI] for redundant
		shr al, cl;
		rol ax, cl;
		ror al, cl;
		mov [edi], al;

FIN:
		popad;

	}

}


__forceinline void __fastcall 
bitread(bool* vals, const void* bitsbuf, uint16 len, byte bitsfrom = 0)
{
	ASSERT(bitsbuf);
	ASSERT(bitsfrom < 8);
	ASSERT(vals);
	ASSERT(len > 0);

	_asm {

		pushad;

		// AH for input, AL for output

		mov esi, [bitsbuf];
		mov edi, [vals];
		mov dx, [len];

		mov ah, [esi];
		inc esi;

#ifndef BITREAD_FROM0
		// ignore bits before ibSrcFrom
		mov cl, [bitsfrom];
		shr ah, cl;
		mov ch, 8;
		sub ch, cl;
#else
		mov ch, 8;
#endif

LOP:
		// CL is the target offset now
		// CH is the bit length of each loop

		sub dl, ch;
		sbb dh, 0;
		jge PRC;

		add ch, dl;
		jle FIN;

PRC:
		xor al, al;
		shr ah, 1; //shift BIT-0 into CF
		adc al, 0; //if CF then BL = 1, else BL = 0

		mov [edi], al;
		inc edi;

		dec ch;
		jnz PRC;

		//NXT:
		// load source byte
		mov ah, [esi];
		inc esi;
		mov ch, 8;

		jmp LOP;

FIN:
		popad;

	}

}


//seek index bits that all are true. 
// if iwhere returned -1, it means the position of index is beyond len
__forceinline void __fastcall 
bitfind(int& iwhere, const void* bitsbuf, uint16 len, short index, byte bitsfrom = 0)
{
	ASSERT(bitsbuf);
	ASSERT(bitsfrom < 8);
	ASSERT(len > 0 && index >= 0);

	_asm {

		pushad;

		// AH for input, AL for output

		mov esi, [bitsbuf];
		mov dx, [len];
		mov edi, -1; //EDI as iWhere
		mov ebx, -1; //EBX as iIndex
		xor al, al; //OK/NO

		mov ah, [esi];
		inc esi;

#ifndef BITSEEK_FROM0
		// ignore bits before ibSrcFrom
		mov cl, [bitsfrom];
		shr ah, cl;
		mov ch, 8;
		sub ch, cl;
#else
		mov ch, 8;
#endif

LOP:
		// CL is the target offset now
		// CH is the bit length of each loop

		sub dl, ch;
		sbb dh, 0;
		jge PRC;

		add ch, dl;
		jle FIN;

PRC:
		inc edi; //iWhere increase regardless of true or false. 
		shr ah, 1;
		adc bx, 0; //iIndex increase if came to a true bit. 
		//EBX is accumulated to approach iIndex. 

		not al;
		cmp bx, [index];
		jge FIN;
		not al;

		dec ch;
		jnz PRC;

		//NXT:
		// load source byte
		mov ah, [esi];
		inc esi;
		mov ch, 8;

		jmp LOP;

FIN:
		test al, al;
		jne RES;
		mov edi, -1;
RES:
		mov eax, iwhere;
		mov [eax], edi;
		popad;

	}

}

__forceinline int __fastcall 
bitfind(const void* bitsbuf, uint16 len, short index, byte bitsfrom = 0)
{
	int res;
	bitfind(res, bitsbuf, len, index, bitsfrom);
	return res;
}

template <size_t SIZ>
void sbprintf(char str[SIZ], const void* bitsbuf, uint16 len, byte bitsfrom = 0)
{
	ASSERT(SIZ >= 2);
	bool val[SIZ];
	if (len > SIZ-1)
		len = SIZ-1;
	bitread(val, bitsbuf, len, bitsfrom);
	for (int i=0; i<len; ++i)
		str[i] = (val[i] ? '1' : '0');
	str[SIZ-2] = '*'; //more bits out of string buffer
	str[len] = NULL;
}

template <size_t SIZ>
void wsbprintf(wchar_t str[SIZ], const void* bitsbuf, uint16 len, byte bitsfrom = 0)
{
	ASSERT(SIZ > 0);
	bool val[SIZ];
	if (len > SIZ-1)
		len = SIZ-1;
	bitread(val, bitsbuf, len, bitsfrom);
	for (int i=0; i<len; ++i)
		str[i] = (val[i] ? '1' : '0');
	str[len] = NULL;
	str[SIZ-2] = '*'; //more bits out of string buffer
}

#ifdef UNICODE
#define _stbprintf wsbprintf
#else
#define _stbprintf sbprintf
#endif

#endif //__CR_BIT_STR_ASM_OPTIMIZED_H__